package com.sqlcheck.service.impl;

import com.sqlcheck.service.GitRepositoryService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GitRepositoryServiceImpl implements GitRepositoryService {

    @Value("${app.git.clone-timeout:300}")
    private int cloneTimeoutSeconds;

    @Value("${app.git.max-depth:1}")
    private int maxDepth;

    @Value("${app.git.workspace}")
    private String gitWorkspace;

    @Override
    public File cloneRepository(String repositoryUrl, String branch, String accessToken, String targetDir) {
        try {
            File targetDirectory = new File(targetDir);

            // 如果目标目录已存在且是Git仓库，尝试更新
            if (targetDirectory.exists() && isGitRepository(targetDirectory)) {
                log.info("目标目录已存在Git仓库，尝试更新: {}", targetDir);
                if (updateRepository(targetDirectory, branch)) {
                    return targetDirectory;
                } else {
                    // 更新失败，删除目录重新克隆
                    log.warn("更新仓库失败，删除目录重新克隆: {}", targetDir);
                    deleteRepository(targetDirectory);
                }
            }

            // 确保父目录存在
            File parentDir = targetDirectory.getParentFile();
            if (parentDir != null && !parentDir.exists()) {
                parentDir.mkdirs();
            }

            // 构建克隆命令
            var cloneCommand = Git.cloneRepository()
                    .setURI(repositoryUrl)
                    .setDirectory(targetDirectory)
                    .setBranch(branch)
                    .setDepth(maxDepth)
                    .setTimeout(cloneTimeoutSeconds);

            // 如果提供了访问令牌，设置认证
            if (accessToken != null && !accessToken.trim().isEmpty()) {
                cloneCommand.setCredentialsProvider(
                        new UsernamePasswordCredentialsProvider("oauth2", accessToken));
            }

            // 执行克隆
            log.info("开始克隆Git仓库: {} -> {}", repositoryUrl, targetDir);
            try (Git git = cloneCommand.call()) {
                log.info("Git仓库克隆成功: {}", targetDir);
                return targetDirectory;
            }

        } catch (GitAPIException e) {
            log.error("克隆Git仓库失败: {} - {}", repositoryUrl, e.getMessage(), e);
            return null;
        } catch (Exception e) {
            log.error("克隆Git仓库异常: {} - {}", repositoryUrl, e.getMessage(), e);
            return null;
        }
    }

    @Override
    public boolean updateRepository(File localRepoDir, String branch) {
        try {
            if (!isGitRepository(localRepoDir)) {
                log.error("目录不是有效的Git仓库: {}", localRepoDir.getAbsolutePath());
                return false;
            }

            try (Git git = Git.open(localRepoDir)) {
                // 检查并切换分支
                String currentBranch = getCurrentBranch(localRepoDir);
                if (!branch.equals(currentBranch)) {
                    if (!checkoutBranch(localRepoDir, branch)) {
                        log.error("切换分支失败: {} -> {}", currentBranch, branch);
                        return false;
                    }
                }

                // 拉取最新代码
                log.info("更新Git仓库: {} (分支: {})", localRepoDir.getAbsolutePath(), branch);
                git.pull()
                        .setTimeout(cloneTimeoutSeconds)
                        .call();

                log.info("Git仓库更新成功: {}", localRepoDir.getAbsolutePath());
                return true;
            }

        } catch (GitAPIException e) {
            log.error("更新Git仓库失败: {} - {}", localRepoDir.getAbsolutePath(), e.getMessage(), e);
            return false;
        } catch (Exception e) {
            log.error("更新Git仓库异常: {} - {}", localRepoDir.getAbsolutePath(), e.getMessage(), e);
            return false;
        }
    }

    @Override
    public boolean deleteRepository(File localRepoDir) {
        try {
            if (localRepoDir == null || !localRepoDir.exists()) {
                return true;
            }

            log.info("删除Git仓库目录: {}", localRepoDir.getAbsolutePath());
            deleteDirectoryRecursively(localRepoDir.toPath());

            log.info("Git仓库目录删除成功: {}", localRepoDir.getAbsolutePath());
            return true;

        } catch (Exception e) {
            log.error("删除Git仓库目录失败: {} - {}", localRepoDir.getAbsolutePath(), e.getMessage(), e);
            return false;
        }
    }

    @Override
    public String getCurrentBranch(File localRepoDir) {
        try {
            if (!isGitRepository(localRepoDir)) {
                return null;
            }

            try (Git git = Git.open(localRepoDir)) {
                return git.getRepository().getBranch();
            }

        } catch (Exception e) {
            log.error("获取当前分支失败: {} - {}", localRepoDir.getAbsolutePath(), e.getMessage(), e);
            return null;
        }
    }

    @Override
    public List<String> getAllBranches(File localRepoDir) {
        List<String> branches = new ArrayList<>();

        try {
            if (!isGitRepository(localRepoDir)) {
                return branches;
            }

            try (Git git = Git.open(localRepoDir)) {
                List<Ref> branchRefs = git.branchList().call();

                branches = branchRefs.stream()
                        .map(ref -> ref.getName().replace("refs/heads/", ""))
                        .collect(Collectors.toList());
            }

        } catch (Exception e) {
            log.error("获取分支列表失败: {} - {}", localRepoDir.getAbsolutePath(), e.getMessage(), e);
        }

        return branches;
    }

    @Override
    public boolean checkoutBranch(File localRepoDir, String branch) {
        try {
            if (!isGitRepository(localRepoDir)) {
                log.error("目录不是有效的Git仓库: {}", localRepoDir.getAbsolutePath());
                return false;
            }

            try (Git git = Git.open(localRepoDir)) {
                log.info("切换分支: {} -> {}", localRepoDir.getAbsolutePath(), branch);

                git.checkout()
                        .setName(branch)
                        .call();

                log.info("分支切换成功: {}", branch);
                return true;
            }

        } catch (GitAPIException e) {
            log.error("切换分支失败: {} -> {} - {}", localRepoDir.getAbsolutePath(), branch, e.getMessage(), e);
            return false;
        } catch (Exception e) {
            log.error("切换分支异常: {} -> {} - {}", localRepoDir.getAbsolutePath(), branch, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public String getLatestCommit(File localRepoDir) {
        try {
            if (!isGitRepository(localRepoDir)) {
                return null;
            }

            try (Git git = Git.open(localRepoDir)) {
                Repository repository = git.getRepository();

                try (RevWalk revWalk = new RevWalk(repository)) {
                    RevCommit latestCommit = revWalk.parseCommit(repository.resolve("HEAD"));
                    return latestCommit.getId().getName();
                }
            }

        } catch (Exception e) {
            log.error("获取最新提交失败: {} - {}", localRepoDir.getAbsolutePath(), e.getMessage(), e);
            return null;
        }
    }

    @Override
    public boolean isGitRepository(File dir) {
        try {
            if (dir == null || !dir.exists() || !dir.isDirectory()) {
                return false;
            }

            File gitDir = new File(dir, ".git");
            if (!gitDir.exists()) {
                return false;
            }

            // 尝试打开仓库验证
            try (Repository repository = new FileRepositoryBuilder()
                    .setGitDir(gitDir)
                    .build()) {
                return repository.getObjectDatabase().exists();
            }

        } catch (Exception e) {
            return false;
        }
    }

    private void deleteDirectoryRecursively(Path path) throws IOException {
        if (Files.exists(path)) {
            Files.walk(path)
                    .sorted((a, b) -> -a.compareTo(b)) // 逆序删除，先删除文件再删除目录
                    .forEach(p -> {
                        try {
                            Files.delete(p);
                        } catch (IOException e) {
                            log.warn("删除文件失败: {} - {}", p, e.getMessage());
                        }
                    });
        }
    }

    @Override
    public void cleanupGitResources(Long taskId) {
        try {
            String repoDir = gitWorkspace + "/task-" + taskId;
            File repoDirectory = new File(repoDir);

            if (repoDirectory.exists()) {
                boolean deleted = deleteRepository(repoDirectory);
                if (deleted) {
                    log.info("Git资源清理成功: taskId={}", taskId);
                } else {
                    log.warn("Git资源清理失败: taskId={}", taskId);
                }
            }
        } catch (Exception e) {
            log.error("清理Git资源异常: taskId={} - {}", taskId, e.getMessage(), e);
        }
    }

}